/**
 * BibSonomy-Database - Database for BibSonomy.
 *
 * Copyright (C) 2006 - 2016 Knowledge & Data Engineering Group,
 *                               University of Kassel, Germany
 *                               http://www.kde.cs.uni-kassel.de/
 *                           Data Mining and Information Retrieval Group,
 *                               University of Würzburg, Germany
 *                               http://www.is.informatik.uni-wuerzburg.de/en/dmir/
 *                           L3S Research Center,
 *                               Leibniz University Hannover, Germany
 *                               http://www.l3s.de/
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.bibsonomy.database.systemstags;

import static org.bibsonomy.util.ValidationUtils.present;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.bibsonomy.database.systemstags.executable.ExecutableSystemTag;
import org.bibsonomy.database.systemstags.markup.MarkUpSystemTag;
import org.bibsonomy.database.systemstags.search.SearchSystemTag;
import org.bibsonomy.model.Tag;

/**
 * @author Andreas Koch
 */
public class SystemTagFactory {
	
	private static final SystemTagFactory singleton = new SystemTagFactory();

	/**
	 * @return the singleton 
	 */
	public static SystemTagFactory getInstance() {
		return singleton;
	}

	/** The map that contains all executable systemTags */
	private Map<String, ExecutableSystemTag> executableSystemTagMap;
	
	/** The set that contains all searchSystemTags */
	private Map<String, SearchSystemTag> searchSystemTagMap;
	
	/** The set that contains all searchSystemTags */
	private Map<String, MarkUpSystemTag> markUpSystemTagMap;

	/**
	 * Fills the ExecutableSystemTagMap with pairs:
	 * SystemTagName -> instance of the corresponding SystemTag
	 * @param executableSystemTags
	 */
	public void setExecutableSystemTags(final Set<ExecutableSystemTag> executableSystemTags) {
		if(!present(this.executableSystemTagMap)) {
			this.executableSystemTagMap = new HashMap<String, ExecutableSystemTag>();
		}
		for (ExecutableSystemTag sysTag: executableSystemTags) {
			this.executableSystemTagMap.put(sysTag.getName(), sysTag);
		}
	}

	/**
	 * @return a Set containing all executable systemTags
	 * 
	 */
	public Set<ExecutableSystemTag> getExecutableSystemTags(){
		return new HashSet<ExecutableSystemTag>(this.executableSystemTagMap.values());
	}

	/**
	 * Fills the SearchSystemTagMap with pairs:
	 * SystemTagName -> instance of the corresponding SystemTag
	 * @param searchSystemTags
	 */
	public void setSearchSystemTags(final Set<SearchSystemTag> searchSystemTags) {
		if (!present(this.searchSystemTagMap)) {
			this.searchSystemTagMap = new HashMap<String, SearchSystemTag>();
		}
		for (SearchSystemTag sysTag: searchSystemTags) {
			this.searchSystemTagMap.put(sysTag.getName(), sysTag);
		}
	}

	/**
	 * @return a Set containing all search systemTags
	 */
	public Set<SearchSystemTag> getSearchSystemTags(){
		return new HashSet<SearchSystemTag>(this.searchSystemTagMap.values());
	}
	
	/**
	 * Fills the MarkUpSystemTagMap with pairs:
	 * SystemTagName -> instance of the corresponding SystemTag
	 * @param markUpSystemTags 
	 */
	public void setMarkUpSystemTags(final Set<MarkUpSystemTag> markUpSystemTags) {
		if(!present(this.markUpSystemTagMap)) {
			this.markUpSystemTagMap = new HashMap<String, MarkUpSystemTag>();
		}
		for (MarkUpSystemTag sysTag: markUpSystemTags) {
			this.markUpSystemTagMap.put(sysTag.getName(), sysTag);
		}
	}

	/**
	 * @return a Set containing all markUp systemTags
	 */
	public Set<MarkUpSystemTag> getMarkUpSystemTags(){
		return new HashSet<MarkUpSystemTag>(this.markUpSystemTagMap.values());
	}
	
	/**
	 * Returns a new instance of the required systemTag
	 * 
	 * @param tagName = the tag describing the systemTag e. g. "send:xyz" or "sys:for:xyz"
	 * @return an executable system tag or null if 
	 * either the tagType is not in the ExecutableSystemTagMap or
	 * the tagName is not a proper systemTagDescription
	 */
	public ExecutableSystemTag getExecutableSystemTag(final String tagName) {
		final String tagType = SystemTagsUtil.extractType(tagName);
		if (present(tagType)) {
			final ExecutableSystemTag sysTag = this.executableSystemTagMap.get(tagType);
			if (present(sysTag) && sysTag.isInstance(tagName)) {
				// tagName was found and tagName has the correct structure
				ExecutableSystemTag newTag =  sysTag.clone();
				newTag.setArgument(SystemTagsUtil.extractArgument(tagName));
				return newTag;
			}
		}
		return null;
	}

	/**
	 * Returns a new instance of the required systemTag
	 * 
	 * @param tagName the tag describing the systemTag e. g. "user" or "days"
	 * @return a search system tag
	 */
	public SearchSystemTag getSearchSystemTag(final String tagName) {
		final String tagType = SystemTagsUtil.extractType(tagName);
		if (present(tagType)) {
			final SearchSystemTag sysTag = this.searchSystemTagMap.get(tagType);
			if (present(sysTag) && sysTag.isInstance(tagName)) {
				// tagName was found and tagName has the correct structure
				return sysTag.newInstance();
			}
		}
		return null;
	}

	/**
	 * Returns a new instance of the required systemTag
	 * @param tagName
	 * @return get MarkUp system tag
	 */
	public MarkUpSystemTag getMarkUpSystemTag(String tagName) {
		final String tagType = SystemTagsUtil.extractType(tagName);
		if (present(tagType)) {
			final MarkUpSystemTag sysTag = this.markUpSystemTagMap.get(tagType);
			if (present(sysTag) && sysTag.isInstance(tagName)) {
				// tagName was found and tagName has the correct structure
				return sysTag.newInstance();
			}
		}
		return null;
	}
	/**
	 * Determines whether a tag (given by name) is an executable systemTag
	 * 
	 * @param tagName
	 * @return <code>true</code> iff it's an executable system tag
	 */
	public boolean isExecutableSystemTag(final String tagName) {
		final String tagType = SystemTagsUtil.extractType(tagName);
		if (present(tagType)) {
			final ExecutableSystemTag sysTag = this.executableSystemTagMap.get(tagType);
			if (present(sysTag)) {
				/*
				 *  the tagName refers to a systemTag 
				 *  => check if it also has the required structure
				 */
				return sysTag.isInstance(tagName);
			}
		}
		return false;
	}

	/**
	 * Determines whether a tag (given by name) is a systemTag
	 * 
	 * @param tagName
	 * @return <code>true</code> iff it's a search system tag
	 */
	public boolean isSearchSystemTag(final String tagName) {
		final String tagType = SystemTagsUtil.extractType(tagName);
		if (present(tagType)) {
			final SearchSystemTag sysTag = this.searchSystemTagMap.get(tagType);
			if (present(sysTag)) {
				/*
				 *  the tagName refers to a systemTag 
				 *  => check if it also has the required structure
				 */
				return sysTag.isInstance(tagName);
			}
		}
		return false;
	}

	/**
	 * Determines whether a tag (given by name) is a systemTag
	 * 
	 * @param tagName
	 * @return <code>true</code> iff it's a markup system tag
	 */
	public boolean isMarkUpSystemTag(final String tagName) {
		final String tagType = SystemTagsUtil.extractType(tagName);
		if (present(tagType)) {
			final SystemTag sysTag = this.markUpSystemTagMap.get(tagType);
			if (present(sysTag)) {
				/*
				 *  the tagName refers to a systemTag 
				 *  => check if it also has the required structure
				 */
				return sysTag.isInstance(tagName);
			}
		}
		return false;
	}
	
	/**
	 * returns all 
	 * @param tag
	 * @return the correct system tag representation of the tag
	 */
	public SystemTag createSystemTag(final Tag tag) {
		final String tagName = tag.getName();
		final MarkUpSystemTag markup = SystemTagsUtil.createMarkUpSystemTag(tagName);
		if (present(markup)) {
			return markup;
		}
		final ExecutableSystemTag exec = SystemTagsUtil.createExecutableTag(tag);
		if (present(exec)) {
			return exec;
		}
		final SearchSystemTag search = SystemTagsUtil.createSearchSystemTag(tagName);
		if (present(search)) {
			return search;
		}
		return null;
	}
}
